package com.tangji.signaltower.websocket.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 * User: Amarendar
 * Date: 2020/6/2
 * Time: 下午2:15
 */
@Component
public class NettyServer {

    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

    @Value("9001")
    private int port;

    private boolean start = false;

    public boolean isStart() {
        return start;
    }

    public void run() {
        try {
            logger.info("启动监听..."+port);
            //bossGroup线程池则只是在 Bind 某个端口后，获得其中一个线程作为 MainReactor，专门处理端口的 Accept 事件，每个端口对应一个 Boss 线程
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            //workerGroup 线程池会被各个 SubReactor 和 Worker 线程充分利用
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap server = new ServerBootstrap();
                server.group(bossGroup, workerGroup);
                server.channel(NioServerSocketChannel.class);
                server.option(ChannelOption.SO_BACKLOG, 1024);
                server.childOption(ChannelOption.SO_KEEPALIVE, true);
                server.childOption(ChannelOption.TCP_NODELAY, true);
                server.localAddress(this.port);
                server.childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        System.out.println("收到新连接:" + ch.localAddress() + ",客户端地址:" + ch.remoteAddress());
                        ch.pipeline().addLast(new HttpServerCodec());
                        ch.pipeline().addLast(new ChunkedWriteHandler());
                        ch.pipeline().addLast(new IPHandler());
                        ch.pipeline().addLast(new HttpObjectAggregator(65536));
                        ch.pipeline().addLast(new WebSocketServerCompressionHandler());
                        ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", "WebSocket", true, 65536 * 10));
                        ch.pipeline().addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                        ch.pipeline().addLast(new HeartBeatServerHandler());
                        ch.pipeline().addLast(new WebSocketServiceHandler());
                    }
                });

                ChannelFuture cf = server.bind(port).addListener(future -> {
                    if (future.isSuccess()) {
                        System.out.println(new Date() + ": 端口[" + port + "]绑定成功!");
                    } else {
                        System.err.println("端口[" + port + "]绑定失败!");
                    }
                }).sync(); // 服务器异步创建绑定

                this.start = true;
                cf.channel().closeFuture().sync(); // 关闭服务器通道 让线程进入wait状态，也就是暂时不会执行到finally里面，nettyserver也持续运行，如果监听到关闭事件，可以优雅的关闭通道和nettyserver
            } finally {
                workerGroup.shutdownGracefully().sync(); //释放线程池资源
                bossGroup.shutdownGracefully().sync();
            }
        } catch (Exception e) {
            logger.error("启动监听失败...", e);
        }
    }
}
